#include "chassisInfo.h"
#include <cstring>
#include <iostream>

ChassisInfo::~ChassisInfo()
{
    if (_containedElement != nullptr) {
        delete[] _containedElement;
        _containedElement = nullptr;
    }
}

void        
ChassisInfo::setGeneral(UINT8 len, const std::string& manuf, UINT8 type,
                        const std::string& version, const std::string& sn, 
                        const std::string& tag)
{
    _length = len;
    _manufacturer = manuf;
    _type = type;
    _version = version;
    _serialNumber = sn;
    _assetTagNumber = tag;
}
void        
ChassisInfo::set2_1(UINT8 bootupState, UINT8 powerSupply,
                    UINT8 thermalState, UINT8 securityStatus)
{
    _bootupState = bootupState;
    _powerSupplyState = powerSupply;
    _thermalState = thermalState;
    _securityStatus = securityStatus;
}
// ce: Contained Element
void        
ChassisInfo::setContainedElement(UINT8 count, UINT8 recordLength, const UINT8* ce)
{
    _ceCountN = count;
    _ceRecordLengthM = recordLength;
    if (count > 0 && recordLength >= 0x03) {
        if (_containedElement != nullptr)
            delete[] _containedElement;
        _containedElement = new UINT8[count * recordLength];

        memcpy(_containedElement, ce, count * recordLength);

    }
}

void        
ChassisInfo::print()
{
    if (_length < 0x09)
        return;
    std::cout << "Chassis Information:" << std::endl;
    std::cout << "\tManufacturer: " << _manufacturer << std::endl;
    std::cout << "\tType: " << printType(_type) << std::endl;
    std::cout << "\tLock: " << printLock(_type >> 7) << std::endl;
    std::cout << "\tVersion: " << _version << std::endl;
    std::cout << "\tSerial Number: " << _serialNumber << std::endl;
    std::cout << "\tAsset Tag: " << _assetTagNumber << std::endl;

    if (_length < 0x0D)
        return;
    std::cout << "\tBoot-up State:" << printState(_bootupState) << std::endl;
    std::cout << "\tPower Supply State:" << printState(_powerSupplyState) << std::endl;
    std::cout << "\tThermal State:" << printState(_thermalState) << std::endl;

    std::cout << "\tSecurity Status:" << printSecurityStatus() << std::endl;

    if (_length < 0x11)
        return;
    printf("\tOEM Information: 0x%08X\n", _oemDefined);
    if (_length < 0x13)
        return;
    
    if (_height == 0x00)
        std::cout << "\tHeight: Unspecified" << std::endl;
    else
        printf("\tHeight: %u U\n", _height);
    
    if (_numberOfPowerCords == 0x00)
		std::cout << "\tNumber Of Power Cords: Unspecified" << std::endl;
	else
        printf("\tNumber Of Power Cords: %u\n", _numberOfPowerCords);
    
    if (_length < 0x15)
        return;
    if (_length < (0x15 + _ceCountN * _ceRecordLengthM))
        return;
    printChassisElements(_ceCountN, _ceRecordLengthM, _containedElement);
    if (_length < (0x16 + _ceCountN * _ceRecordLengthM))
        return;
     std::cout << "\tSKU Number:" << _skuNumber << std::endl;
}

void 
ChassisInfo::printChassisElements(UINT8 count, UINT8 len, const UINT8 *p)
{
	int i;

	printf("\tContained Elements: (%u)\n", count);
	for (i = 0; i < count; i++)	{
		if (len >= 0x03) {
			const char *type;
			type = (p[i * len] & 0x80) ?
				smbiosStructureType(p[i * len] & 0x7F) :
				getBaseBoardType(p[i * len] & 0x7F);

			if (p[1 + i * len] == p[2 + i * len])
				printf("\t\t%s (%u)", type, p[1 + i * len]);
			else
                printf("\t\t%s (%u-%u)", type, p[1 + i * len], p[2 + i * len]);
        }
    }
    std::cout << std::endl;
}

const char *
ChassisInfo::getBaseBoardType(UINT8 code)
{
	/* 7.3.2 */
	static const char *type[] = {
		"Unknown", /* 0x01 */
		"Other",
		"Server Blade",
		"Connectivity Switch",
		"System Management Module",
		"Processor Module",
		"I/O Module",
		"Memory Module",
		"Daughter Board",
		"Motherboard",
		"Processor+Memory Module",
		"Processor+I/O Module",
		"Interconnect Board" /* 0x0D */
	};

	if (code >= 0x01 && code <= 0x0D)
		return type[code - 0x01];
	return OUT_OF_SPEC;
}

const char *
ChassisInfo::smbiosStructureType(UINT8 code)
{
	static const char *type[] = {
		"BIOS", /* 0 */
		"System",
		"Base Board",
		"Chassis",
		"Processor",
		"Memory Controller",
		"Memory Module",
		"Cache",
		"Port Connector",
		"System Slots",
		"On Board Devices",
		"OEM Strings",
		"System Configuration Options",
		"BIOS Language",
		"Group Associations",
		"System Event Log",
		"Physical Memory Array",
		"Memory Device",
		"32-bit Memory Error",
		"Memory Array Mapped Address",
		"Memory Device Mapped Address",
		"Built-in Pointing Device",
		"Portable Battery",
		"System Reset",
		"Hardware Security",
		"System Power Controls",
		"Voltage Probe",
		"Cooling Device",
		"Temperature Probe",
		"Electrical Current Probe",
		"Out-of-band Remote Access",
		"Boot Integrity Services",
		"System Boot",
		"64-bit Memory Error",
		"Management Device",
		"Management Device Component",
		"Management Device Threshold Data",
		"Memory Channel",
		"IPMI Device",
		"Power Supply",
		"Additional Information",
		"Onboard Device",
		"Management Controller Host Interface",
		"TPM Device",
		"Processor",
		"Firmware",
		"String Property" /* 46 */
	};

	if (code >= 128)
		return "OEM-specific";
	if (code <= 46)
		return type[code];
	return OUT_OF_SPEC;
}

const char *
ChassisInfo::printType(UINT8 code)
{
	/* 7.4.1 */
	static const char *type[] = {
		"Other", /* 0x01 */
		"Unknown",
		"Desktop",
		"Low Profile Desktop",
		"Pizza Box",
		"Mini Tower",
		"Tower",
		"Portable",
		"Laptop",
		"Notebook",
		"Hand Held",
		"Docking Station",
		"All In One",
		"Sub Notebook",
		"Space-saving",
		"Lunch Box",
		"Main Server Chassis", /* CIM_Chassis.ChassisPackageType says "Main System Chassis" */
		"Expansion Chassis",
		"Sub Chassis",
		"Bus Expansion Chassis",
		"Peripheral Chassis",
		"RAID Chassis",
		"Rack Mount Chassis",
		"Sealed-case PC",
		"Multi-system",
		"CompactPCI",
		"AdvancedTCA",
		"Blade",
		"Blade Enclosing",
		"Tablet",
		"Convertible",
		"Detachable",
		"IoT Gateway",
		"Embedded PC",
		"Mini PC",
		"Stick PC" /* 0x24 */
	};

	code &= 0x7F; /* bits 6:0 are chassis type, 7th bit is the lock bit */

	if (code >= 0x01 && code <= 0x24)
		return type[code - 0x01];
	return OUT_OF_SPEC;
}

const char *
ChassisInfo::printLock(UINT8 code)
{
	static const char *lock[] = {
		"Not Present", /* 0x00 */
		"Present" /* 0x01 */
	};

	return lock[code];
}

const char *
ChassisInfo::printState(UINT8 code)
{
	/* 7.4.2 */
	static const char *state[] = {
		"Other", /* 0x01 */
		"Unknown",
		"Safe",
		"Warning",
		"Critical",
		"Non-recoverable" /* 0x06 */
	};

	if (code >= 0x01 && code <= 0x06)
		return state[code - 0x01];
	return OUT_OF_SPEC;
}

const char *
ChassisInfo::printSecurityStatus()
{
	/* 7.4.3 */
	static const char *status[] = {
		"Other", /* 0x01 */
		"Unknown",
		"None",
		"External Interface Locked Out",
		"External Interface Enabled" /* 0x05 */
	};

	if (_securityStatus >= 0x01 && _securityStatus <= 0x05)
		return status[_securityStatus - 0x01];
	return OUT_OF_SPEC;
}
